#include "flyby.h"

#include "graphics/graphicsmanager.h"
#include "graphics/renderer.h"
#include "graphics/indexbuffer.h"
#include "graphics/vertexbuffer.h"
#include "graphics.opengl/renderer.h"
#include "graphics/light.h"
#include "graphics/texturemanager.h"
#include "graphics.opengl/texture.h"
#include "graphics/texture.h"

#include "inputs/manager.h"
#include "system/timer.h"

#include "application.h"

#include "serviceworld.h"
#include "servicephysics.h"
#include "servicegraphics.h"
#include "servicecamera.h"
#include "serviceentity.h"

#include "planetcache.h"
#include "faceid.h"
#include "helpers.h"




using namespace dw::inputs;

namespace infinity {

	class ViewedArea : public ServiceWorld::AreaOfInterest
	{
	public : //! @name State
		//! @{
		virtual void getAABBox(CellsBox& aabb) const
		{
			dw::float32 cameraFarClip = viewDistance;
			Vector3f camPos = Vector3f(transform.pos);
			Vector3f camDir = Vector3f(transform.at);
			Vector3f camUp = Vector3f(transform.up);
			Vector3f camRight = Vector3f(transform.right);
			Vector3f camFar = camPos + cameraFarClip * camDir;

			dw::float32 camAlpha = camera->getFov();
			dw::float32 tan = tanf(camAlpha * 0.5f);
			dw::float32 heightFar = cameraFarClip * tan;
			dw::float32 widthFar = cameraFarClip * tan * camera->getAspectRatio();

			Vector3f camFarTopLeft = camFar + (camUp * heightFar) + (camRight * widthFar);
			Vector3f camFarBottomRight = camFar - (camUp * heightFar) - (camRight * widthFar);

			Vector3f min;
			Vector3f max;

			min.x = std::min(std::min(std::min(camPos.x, camFar.x), camFarTopLeft.x), camFarBottomRight.x);
			min.y = std::min(std::min(std::min(camPos.y, camFar.y), camFarTopLeft.y), camFarBottomRight.y);
			min.z = std::min(std::min(std::min(camPos.z, camFar.z), camFarTopLeft.z), camFarBottomRight.z);

			max.x = std::max(std::max(std::max(camPos.x, camFar.x), camFarTopLeft.x), camFarBottomRight.x);
			max.y = std::max(std::max(std::max(camPos.y, camFar.y), camFarTopLeft.y), camFarBottomRight.y);
			max.z = std::max(std::max(std::max(camPos.z, camFar.z), camFarTopLeft.z), camFarBottomRight.z);

			aabb.min = Helpers::positionToCell(min) - Vector3i(1, 1, 1);
			aabb.max = Helpers::positionToCell(max) + Vector3i(1, 1, 1);
		}

		virtual dw::float32 getPriority(const CellsBox& aabb) const
		{
			Vector3f camPos(transform.pos);
			Vector3f closestPoint;

			closestPoint.x = std::max((dw::float32)aabb.min.x, std::min((dw::float32)aabb.max.x, camPos.x));
			closestPoint.y = std::max((dw::float32)aabb.min.y, std::min((dw::float32)aabb.max.y, camPos.y));
			closestPoint.z = std::max((dw::float32)aabb.min.z, std::min((dw::float32)aabb.max.z, camPos.z));

			return (closestPoint - camPos).length() / viewDistance;
		}

		dw::float32 viewDistance;
		Matrix4f transform;
		dw::graphics::Camera* camera;
		//! @}
	};

	enum IntersectionType
	{
		INVALID = -1,
		CELL,
		ITEM
	};

	struct Intersection
	{
		dw::float32 distance;
		Vector3i cellId;
		Planet::Cell cell;
		Planet::Item item;
		FaceId cellFace;
		IntersectionType type;
	};

	ViewedArea g_viewedArea;

FlyBy::FlyBy()
:	GameMode("FlyBy")
,	m_blockType(1)
,	m_entity(NULL)
,	m_TimeDelete(0)
,   m_LapsTimeDelete(1.5f)
,   m_build(false)
,	m_showInventaire(false)
,	m_pSpellBar(NULL)
,	m_pInventaire(NULL)

{
	m_changeLightingMode.addCondition(KeyStateCondition(OIS::KC_F2, ButtonStateCondition::Pressed));
	m_save.addCondition(KeyStateCondition(OIS::KC_F3, ButtonStateCondition::Pressed));

	m_camDebug.addCondition(KeyStateCondition(OIS::KC_F9, ButtonStateCondition::Pressed));
	m_camFps.addCondition(KeyStateCondition(OIS::KC_F10, ButtonStateCondition::Pressed));

	// Picking command
	m_leftClick.addCondition(MouseButtonCondition(OIS::MB_Left, ButtonStateCondition::Down));
	m_leftClick.addSampler(MouseCursorSampler(MouseCursorSampler::X, MouseCursorSampler::Absolute));
	m_leftClick.addSampler(MouseCursorSampler(MouseCursorSampler::Y, MouseCursorSampler::Absolute));

	m_rightClick.addCondition(MouseButtonCondition(OIS::MB_Right, ButtonStateCondition::Pressed));
	m_rightClick.addSampler(MouseCursorSampler(MouseCursorSampler::X, MouseCursorSampler::Absolute));
	m_rightClick.addSampler(MouseCursorSampler(MouseCursorSampler::Y, MouseCursorSampler::Absolute));

	m_chooseBlock.addSampler(MouseCursorSampler(MouseCursorSampler::Z, MouseCursorSampler::Relative));

	m_spawnEntity.addCondition(MouseButtonCondition(OIS::MB_Left, ButtonStateCondition::Pressed));
	m_spawnEntity.addCondition(KeyStateCondition(OIS::KC_LCONTROL, ButtonStateCondition::Down));

	m_dropAll.addCondition(KeyStateCondition(OIS::KC_Q, ButtonStateCondition::Pressed));

	m_grenadeEntity.addCondition(KeyStateCondition(OIS::KC_E, ButtonStateCondition::Pressed));
	
	m_buildaction.addCondition(KeyStateCondition(OIS::KC_C, ButtonStateCondition::Pressed));

	m_inventaireaction.addCondition(KeyStateCondition(OIS::KC_I, ButtonStateCondition::Pressed));


	m_customState.value(dw::graphics::Renderer::RS_TEXTURE_2D) = true;
	m_customState.value(dw::graphics::Renderer::RS_CULLING) = true;
	m_customState.value(dw::graphics::Renderer::RS_SHADE_MODE) = dw::graphics::Renderer::ShadeMode::GOURAUD;
	m_customState.value(dw::graphics::Renderer::RS_FILL_MODE) = dw::graphics::Renderer::FillMode::SOLID;
	m_customState.value(dw::graphics::Renderer::RS_LIGHTING) = true;
	m_customState.value(dw::graphics::Renderer::RS_DEPTH_TEST) = true;
	m_customState.value(dw::graphics::Renderer::RS_DEPTH_FUNC) = dw::graphics::Renderer::LESS;
}

FlyBy::~FlyBy()
{
	if (m_entity != NULL)
	{
		ServiceEntity& serviceEntity = Application::getInstance().getService< ServiceEntity >();
		serviceEntity.condemnEntity(m_entity);

		delete m_pInventaire;
		delete m_pSpellBar;	
	}

}

void FlyBy::install()
{
 	InputManager& inputManager(InputManager::getInstance());
 	
	inputManager.registerCommand(&m_changeLightingMode);
	inputManager.registerCommand(&m_save);

	inputManager.registerCommand(&m_camDebug);
	inputManager.registerCommand(&m_camFps);

	inputManager.registerCommand(&m_leftClick);
	inputManager.registerCommand(&m_rightClick);
	
	inputManager.registerCommand(&m_chooseBlock);
	inputManager.registerCommand(&m_spawnEntity);
	inputManager.registerCommand(&m_grenadeEntity);

	inputManager.registerCommand(&m_buildaction);
	inputManager.registerCommand(&m_inventaireaction);

	inputManager.registerCommand(&m_dropAll);


	ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
	dw::graphics::Camera* camera = svcCamera.getActiveCameraController()->getCamera();
	dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();

	render::CellClusterManager& cellClustermng(render::CellClusterManager::getInstance());
	cellClustermng.setCameraNode(cameraNode);
	WorldGridManager::getInstance().getPlanet()->registerListener(&cellClustermng);

	g_viewedArea.camera = camera;
	g_viewedArea.viewDistance = 64;
	g_viewedArea.transform = cameraNode->getTransform();

	dw::graphics::TextureManager::getInstance().loadTexture("items.png");
	dw::graphics::TextureManager::getInstance().loadTexture("terrain.png");
	// ServiceWorld* svcWorld = Application::getInstance().getService< ServiceWorld >();
	// svcWorld->registerAreaOfInterest(&g_viewedArea);
}

static bool intersect(const dw::maths::Ray& ray, const Vector3f min, const Vector3f max, Intersection& intersection)
{
	dwAssert(ray.direction.isNormalized(), "Invalid parameter: ray.direction should be normalized");

	dw::float32 tNear = 0.0f;
	dw::float32 tFar = intersection.distance;

	dw::uint32 tAxis = -1;
	dw::int32 tDirection = 0;

	for (dw::uint32 axisIt = 0; axisIt < 3; ++axisIt)
	{
		dw::float32 relativeDirection = ray.direction[axisIt];

		if (relativeDirection != 0.0f)
		{
			dw::float32 invDirection = 1.0f / relativeDirection;
			dw::float32 axisNear;
			dw::float32 axisFar;

			if (relativeDirection > 0.0f)
			{
				axisNear = (min[axisIt] - ray.position[axisIt]) * invDirection;
				axisFar  = (max[axisIt] - ray.position[axisIt]) * invDirection;
			}
			else
			{
				axisNear = (max[axisIt] - ray.position[axisIt]) * invDirection;
				axisFar  = (min[axisIt] - ray.position[axisIt]) * invDirection;
			}

			if ((tNear > axisFar) || (axisNear > tFar))
			{
				return false;
			}

			if (axisNear > tNear)
			{
				tNear = axisNear;
				tAxis = axisIt;
				tDirection = (relativeDirection > 0.0f)?1:0;
			}
			if (axisFar < tFar)
			{
				tFar = axisFar;
			}

			if (tNear > tFar || tFar < 1e-6f)
			{
				return false;
			}
		}
	}

	intersection.distance = tNear;
	intersection.cellFace = (FaceId)(tAxis * 2 + tDirection);

	return true;
}

static bool intersect(const dw::maths::Ray& ray, const Planet* planet, Intersection& intersection)
{
	Vector3f cacheExtendF = intersection.distance * ray.direction;
	Vector3f cacheCapacityAbs(std::abs(cacheExtendF.x), std::abs(cacheExtendF.y), std::abs(cacheExtendF.z));
	
	Vector3i cacheCapacity((dw::int32)cacheCapacityAbs.x + 1, (dw::int32)cacheCapacityAbs.y + 1, (dw::int32)cacheCapacityAbs.z + 1);
	Planet::Cache cache(cacheCapacity);

	Vector3i cacheOrigin;

	cacheOrigin.x = (dw::int32)ray.position.x - (ray.position.x < 0.0f ? 1 : 0);
	cacheOrigin.y = (dw::int32)ray.position.y - (ray.position.y < 0.0f ? 1 : 0);
	cacheOrigin.z = (dw::int32)ray.position.z - (ray.position.z < 0.0f ? 1 : 0);

	cacheOrigin.x = cacheExtendF.x < 0.0f ? (cacheOrigin.x - cacheCapacity.x + 1) : (cacheOrigin.x);
	cacheOrigin.y = cacheExtendF.y < 0.0f ? (cacheOrigin.y - cacheCapacity.y + 1) : (cacheOrigin.y);
	cacheOrigin.z = cacheExtendF.z < 0.0f ? (cacheOrigin.z - cacheCapacity.z + 1) : (cacheOrigin.z);

	cache.prefetch(planet, cacheOrigin, cacheCapacity);

	Vector3i cellCoords;
	Planet::Cell cell;
	Planet::Item item;

	Vector3f min;
	Vector3f max;

	bool hit = false;

	for (dw::int32 k = cacheOrigin.z; k < cacheOrigin.z + cacheCapacity.z; ++k)
	{
		cellCoords.z = k;

		min.z = cellCoords.z;
		max.z = cellCoords.z + 1;
		for (dw::int32 j = cacheOrigin.y; j < cacheOrigin.y + cacheCapacity.y; ++j)
		{
			cellCoords.y = j;
			
			min.y = cellCoords.y;
			max.y = cellCoords.y + 1;
			for (dw::int32 i = cacheOrigin.x; i < cacheOrigin.x + cacheCapacity.x; ++i)
			{
				cellCoords.x = i;

				min.x = cellCoords.x;
				max.x = cellCoords.x + 1;

				cache.getCell(cellCoords, cell);

				const Material& material = planet->getMaterialLibrary().getMaterialFromId(cell.getMaterialId());

				if (!material.isVoid())
				{
					if (intersect(ray, min, max, intersection))
					{
						hit = true;
						intersection.cellId = cellCoords;
						intersection.cell = cell;
						intersection.type = CELL;
					}
				}
				else
				{
					if(planet->getItem(cellCoords,item))
					{
						if (intersect(ray, min, max, intersection))
						{
							hit = true;
							intersection.cellId = cellCoords;
							intersection.item = item;
							intersection.type = ITEM;
						}
					}
				}
			}
		}
	}

	return hit;
}

void FlyBy::update(const dw::float32 deltaT)
{
	if (m_changeLightingMode.isActive())
	{
		dw::int32 currentLightMode = m_customState.value(dw::graphics::Renderer::RS_LIGHTING);

		m_customState.value(dw::graphics::Renderer::RS_LIGHTING) = !currentLightMode;
	}

	if (m_save.isActive())
	{
		WorldGridManager::getInstance().getPlanet()->save("./savegame.ip");
	}

	if (m_camDebug.isActive())
	{
		ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
		svcCamera.setActiveCameraController(ServiceCamera::CAMERA_DEBUG);

		dw::graphics::Camera* camera = svcCamera.getActiveCameraController()->getCamera();
		dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();

		render::CellClusterManager& cellClustermng(render::CellClusterManager::getInstance());
		cellClustermng.setCameraNode(cameraNode);
	
		g_viewedArea.camera = camera;
		g_viewedArea.transform = cameraNode->getTransform();

	}

	if (m_camFps.isActive() && m_entity)
	{
		ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
		svcCamera.setActiveCameraController(ServiceCamera::CAMERA_FPS);
		svcCamera.bindEntity(m_entity);


		dw::graphics::Camera* camera = svcCamera.getActiveCameraController()->getCamera();
		dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();

		render::CellClusterManager& cellClustermng(render::CellClusterManager::getInstance());
		cellClustermng.setCameraNode(cameraNode);

		g_viewedArea.camera = camera;
		g_viewedArea.transform = cameraNode->getTransform();
	}

	ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
	dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();

	

	//// choose block /////
	if (m_chooseBlock.isActive())
	{
		const WorldGridManager& worldManager = WorldGridManager::getInstance();
		const MaterialLibrary& materialLib = worldManager.getPlanet()->getMaterialLibrary();

		dw::float32 molette = m_chooseBlock.getValue(0);

		if(m_pSpellBar && !m_build)
		{
			if((int)dw::sign(molette) > 0)
			{
				m_pSpellBar->indexUp();
			}
			else if((int)dw::sign(molette) < 0)
			{
				m_pSpellBar->indexDown();
			}
		}

		if(m_build)
		{
			m_blockType += (int)dw::sign(molette);

			if (m_blockType >= materialLib.getMaterialCount())
			{
				m_blockType = 1;
			}
			else if (m_blockType == 0)
			{
				m_blockType = materialLib.getMaterialCount() - 1;
			}
		}
	}

	///service picking //////////////
	if (m_leftClick.isActive())
	{
		dw::float32 cursorX = m_leftClick.getValue(0);
		dw::float32 cursorY = m_leftClick.getValue(1);

		//! @todo retrieve ray direction from cursorX, cursorY

		const Vector3f position(cameraNode->getTransform().pos);
		const Vector3f direction(cameraNode->getTransform().at);
		dw::maths::Ray ray;
		
		ray.position = position;
		ray.direction = direction;

		Intersection intersection;

		intersection.distance = 15.0f; // max distance

		if (intersect(ray, WorldGridManager::getInstance().getPlanet(), intersection))
		{
			if(intersection.type == ITEM && m_TimeDelete == 0)
			{
				dwLogDebug("item left click \n");
				m_TimeDelete += deltaT;
			}

			if(lastCellIdPicked == intersection.cellId)
			{
				m_TimeDelete += deltaT;
			}
			else
			{
				m_TimeDelete = 0;
			}

			lastCellIdPicked = intersection.cellId;
			
			//dwLogDebug("DeleteBlock[%i, %i, %i] %f\n", intersection.cellId.x, intersection.cellId.y, intersection.cellId.z, m_TimeDelete);

			if(m_TimeDelete > (m_LapsTimeDelete) || m_build)
			{
				if(intersection.type == CELL)
				{
					m_TimeDelete = 0;
					WorldGridManager::getInstance().getPlanet()->setCell(intersection.cellId, Planet::Cell::Default);
				}
				else if(intersection.type == ITEM)
				{
					m_TimeDelete = 0;
					WorldGridManager::getInstance().getPlanet()->removeItem(intersection.cellId);
					// delete item
					dwLogDebug("delete item \n");

				}
			}
		}
	}
	else
	{
		m_TimeDelete = 0.0f;
	}
	
	if (m_rightClick.isActive())
	{
		bool canPlace = false;
		Container::Slot slot;
		if(m_pSpellBar)
		{
			slot = m_entity->getContainer()->getSlot(m_pSpellBar->getIndexSelecteur());
			canPlace = slot.count > 0;
			
			if(slot.object.type == Object::ENTITY)
			{
				const WorldGridManager& worldManager = WorldGridManager::getInstance();
				const ItemMaterialLibrary& materialLib = worldManager.getPlanet()->getItemMaterialLibrary();
				const ItemMaterial& material = materialLib.getMaterialFromId(slot.object.id);

				canPlace &= material.isPlacable();
			}
		}

		//if(m_build || canPlace)
		{
			dw::float32 cursorX = m_rightClick.getValue(0);
			dw::float32 cursorY = m_rightClick.getValue(1);

			//! @todo retrieve ray direction from cursorX, cursorY

			const Vector3f position(cameraNode->getTransform().pos);
			const Vector3f direction(cameraNode->getTransform().at);
			dw::maths::Ray ray;

			ray.position = position;
			ray.direction = direction;

			Intersection intersection;

			intersection.distance = 15.0f; // max distance

			if (intersect(ray, WorldGridManager::getInstance().getPlanet(), intersection))
			{
				if(intersection.type == CELL)
				{
					Vector3i addCellId = intersection.cellId;

					switch(intersection.cellFace)
					{
					case Face_Top:
						{
							addCellId += Vector3i(0,1,0);
							break;
						}
					case Face_Bottom:
						{
							addCellId += Vector3i(0,-1,0);
							break;
						}
					case Face_Left:
						{
							addCellId += Vector3i(1,0,0);
							break;
						}
					case Face_Right:
						{
							addCellId += Vector3i(-1,0,0);
							break;
						}
					case Face_Front:
						{
							addCellId += Vector3i(0,0,1);
							break;
						}
					case Face_Back:
						{
							addCellId += Vector3i(0,0,-1);
							break;
						}
					}

					Planet::Cell newCell;
					if(m_build)
					{
						newCell.setMaterialId(m_blockType);
						WorldGridManager::getInstance().getPlanet()->setCell(addCellId, newCell);
					}
					else if(m_entity && m_pSpellBar && canPlace)
					{
						if(slot.object.type == Object::BLOC)
						{
							newCell.setMaterialId(slot.object.id);
							m_entity->getContainer()->remove(m_pSpellBar->getIndexSelecteur(),1);

							dwLogDebug("AddBlock[%i, %i, %i]\n", addCellId.x, addCellId.y, addCellId.z);
							WorldGridManager::getInstance().getPlanet()->setCell(addCellId, newCell);
						}
						else if(slot.object.type == Object::ENTITY || slot.object.type == Object::ENTITY_BLOC)
						{
							Planet::Item newItem;
							newItem.setItemId(slot.object.id);
							m_entity->getContainer()->remove(m_pSpellBar->getIndexSelecteur(),1);

							dwLogDebug("AddItem[%i, %i, %i]\n", addCellId.x, addCellId.y, addCellId.z);
							WorldGridManager::getInstance().getPlanet()->setItem(addCellId, newItem);
						}
					}			

					
				}
				else if(intersection.type == ITEM)
				{

					dwLogDebug("item right click \n");
					//intersection.item.rightClick();	

					//collect item
					std::vector< Entity * >& livingEntity = Application::getInstance().getService< ServiceEntity >().getLivingEntity();
					std::vector< Entity * >::const_iterator entitiesEnd = livingEntity.end();

					Entity* entity = NULL;
					ItemEntity* itemEntity = NULL;
					dw::maths::Vector4f coordsV4 = dw::maths::Vector4f(intersection.cellId.x, intersection.cellId.y, intersection.cellId.z,1.0f);

					for (std::vector< Entity * >::iterator entityIt = livingEntity.begin(); entityIt != entitiesEnd; ++entityIt)
					{
						entity = *entityIt;
						if (entity->getType() >= Entity::Type_Item )
						{
							itemEntity = static_cast< ItemEntity* >(entity);
							if (itemEntity->getMatrix().pos == coordsV4)
							{
								itemEntity->rightClick();
							}
						}
					}
				}
			}
		}
	}

	if (m_dropAll.isActive())
	{
		if(m_entity && m_pSpellBar)
		{
			m_entity->getContainer()->dropAll(m_pSpellBar->getIndexSelecteur());
		}
	}

	if (m_spawnEntity.isActive())
	{
		ServiceEntity& serviceEntity = Application::getInstance().getService< ServiceEntity >();

		if (m_entity != NULL)
		{
			serviceEntity.condemnEntity(m_entity);
			delete m_pSpellBar;
			delete m_pInventaire;
			
			m_pSpellBar = NULL;
			m_pInventaire = NULL;
		}

		ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
		dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();
		const Matrix4f& cameraTransform = cameraNode->getTransform();
		const Vector3f cameraPos(cameraTransform.pos);
		const Vector3f cameraAt(cameraTransform.at);

		m_entity = serviceEntity.spawnEntity< CharactereEntity >("TestEntity");
		m_entity->setInitialPosition(cameraPos + 2.0f * cameraAt);
		m_entity->setInitialVelocity(cameraAt);

		m_pSpellBar = new render::SpellBar(m_entity->getContainer());
		m_pSpellBar->initializeGUI();

		m_pInventaire = new render::Inventaire(m_entity->getContainer());
		m_pInventaire->initializeGUI();
	}

	//grenade
	if (m_grenadeEntity.isActive())
	{
		ServiceEntity& serviceEntity = Application::getInstance().getService< ServiceEntity >();

		ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
		dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();
		const Matrix4f& cameraTransform = cameraNode->getTransform();
		const Vector3f cameraPos(cameraTransform.pos);
		const Vector3f cameraAt(cameraTransform.at);

		GrenadeEntity* grenadeEntity = serviceEntity.spawnEntity< GrenadeEntity >("grenadeEntity");
		grenadeEntity->setInitialPosition(cameraPos + 2.0f * cameraAt);
		grenadeEntity->setInitialVelocity(25.0f * cameraAt);
	}

	//buildmode
	if (m_buildaction.isActive())
	{
		m_build =  !m_build;
	}

	//inventaire
	if (m_inventaireaction.isActive())
	{
		m_showInventaire =  !m_showInventaire;
	}

	// Picking for highlight
	{
		const Vector3f position(cameraNode->getTransform().pos);
		const Vector3f direction(cameraNode->getTransform().at);
		dw::maths::Ray ray;
		
		ray.position = position;
		ray.direction = direction;

		Intersection intersection;

		intersection.distance = 15.0f; // max distance

		if (intersect(ray, WorldGridManager::getInstance().getPlanet(), intersection))
		{
			m_intersectedCell =  Vector3f(intersection.cellId.x, intersection.cellId.y, intersection.cellId.z);
// 			if(intersection.type == CELL)
// 			{
// 				m_highlight = true;
// 			}
// 			else
			m_highlight = true;
		}
		else
		{
			m_highlight = false;
		}
	}

	if(m_entity && m_entity->getPhysiqueObject())
		g_viewedArea.transform = m_entity->getPhysiqueObject()->getTransform(); /*m_cameraNode.getTransform()*/;

	render::CellClusterManager& cellClustermng(render::CellClusterManager::getInstance());

	cellClustermng.update(deltaT);
}

void FlyBy::render()
{
	ServiceCamera& svcCamera = Application::getInstance().getService< ServiceCamera >();
	dw::graphics::Camera* camera = svcCamera.getActiveCameraController()->getCamera();
	dw::graphics::Node* cameraNode = svcCamera.getActiveCameraController()->getMatrix();

	dw::graphics::Renderer& renderer(dw::graphics::GraphicsManager::getInstance().getRenderer());

	// Custom Render States
	renderer.pushState();
	renderer.setState(m_customState);

	GLfloat LightAmbient[]= { 0.2f, 0.2f, 0.2f, 1.0f };
	GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat LightPosition[]= { -1.0f, -1.0f, -1.0f, 1.0f };

	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);	
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
	glEnable(GL_LIGHT0);

	const dw::system::Window& window = Application::getInstance().getWindow();

	// Projection & View matrices
	const Matrix4f projMtx = camera->getProjectionMatrix();
	const Matrix4f viewMtx = cameraNode->getViewMatrix();
	
	renderer.setMatrixMode(dw::graphics::Renderer::PROJECTION);
	renderer.loadIdentity();
	renderer.multMatrix(projMtx);

	renderer.setMatrixMode(dw::graphics::Renderer::MODELVIEW);
	renderer.loadIdentity();
	renderer.multMatrix(viewMtx);

	const Matrix4f cameraTransform = cameraNode->getTransform();

	glLightfv(GL_LIGHT0, GL_POSITION, cameraTransform.pos.v);


	//test
	if (m_TimeDelete > 0)
	{
		renderer.pushState();
		renderer.pushMatrix();

		renderer.setRenderState(dw::graphics::Renderer::RS_LIGHTING, false);
		renderer.setRenderState(dw::graphics::Renderer::RS_DEPTH_TEST, true);
		renderer.setRenderState(dw::graphics::Renderer::RS_BLENDING, true);
		renderer.setRenderState(dw::graphics::Renderer::RS_BLEND_SRC_FUNC, dw::graphics::Renderer::BlendMode::SRC_ALPHA);
		renderer.setRenderState(dw::graphics::Renderer::RS_BLEND_DST_FUNC, dw::graphics::Renderer::BlendMode::ONE);
		
		glEnable (GL_POLYGON_OFFSET_FILL);
		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
		glPolygonOffset (-1.0f, -1.0f);	

		glTranslatef((dw::float32)m_intersectedCell.x + 0.5f, (dw::float32)m_intersectedCell.y + 0.5f, (dw::float32)m_intersectedCell.z + 0.5f);

		int offset = (int)(m_TimeDelete / (m_LapsTimeDelete * 0.1f));
		dw::maths::Vector2f upleft(offset/16.f,15/16.0f);
		float sizecell = 1/16.0f;

		glBegin(GL_QUADS);
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		// Front Face
		glTexCoord2f(upleft.x, upleft.y); glVertex3f(-0.5f, -0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f( 0.5f, -0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f( 0.5f,  0.5f,  0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f(-0.5f,  0.5f,  0.5f);	// Top Left Of The Texture and Quad
		// Back Face
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f(-0.5f, -0.5f, -0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f(-0.5f,  0.5f, -0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y ); glVertex3f( 0.5f,  0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f( 0.5f, -0.5f, -0.5f);	// Bottom Left Of The Texture and Quad
		// Top Face
		glTexCoord2f(upleft.x + sizecell, upleft.y ); glVertex3f(-0.5f,  0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f(-0.5f,  0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y ); glVertex3f( 0.5f,  0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f( 0.5f,  0.5f, -0.5f);	// Top Right Of The Texture and Quad
		// Bottom Face
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f(-0.5f, -0.5f, -0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f( 0.5f, -0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f( 0.5f, -0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f(-0.5f, -0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		// Right face
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f( 0.5f, -0.5f, -0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f( 0.5f,  0.5f, -0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f( 0.5f,  0.5f,  0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f( 0.5f, -0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		// Left Face
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f(-0.5f, -0.5f, -0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f(-0.5f, -0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f(-0.5f,  0.5f,  0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f(-0.5f,  0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glEnd();

		renderer.popState();
		renderer.popMatrix();
	}

	if (m_highlight)
	{
		renderer.pushState();
		renderer.pushMatrix();

		renderer.setRenderState(dw::graphics::Renderer::RS_LIGHTING, false);
		renderer.setRenderState(dw::graphics::Renderer::RS_DEPTH_TEST, true);
 		renderer.setRenderState(dw::graphics::Renderer::RS_BLENDING, true);
 		renderer.setRenderState(dw::graphics::Renderer::RS_BLEND_SRC_FUNC, dw::graphics::Renderer::BlendMode::SRC_ALPHA);
 		renderer.setRenderState(dw::graphics::Renderer::RS_BLEND_DST_FUNC, dw::graphics::Renderer::BlendMode::ONE);

		glEnable (GL_POLYGON_OFFSET_FILL);
		glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
		glPolygonOffset (-1.0f, -1.0f);

		glTranslatef((dw::float32)m_intersectedCell.x + 0.5f, (dw::float32)m_intersectedCell.y + 0.5f, (dw::float32)m_intersectedCell.z + 0.5f);

		dw::maths::Vector2f upleft(0.0f,2/16.0f);
		float sizecell = 1/16.0f;

		glBegin(GL_QUADS);
		glColor4f(1.0f,1.0f,1.0f,0.3f);
		// Front Face
		glTexCoord2f(upleft.x, upleft.y); glVertex3f(-0.5f, -0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f( 0.5f, -0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f( 0.5f,  0.5f,  0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f(-0.5f,  0.5f,  0.5f);	// Top Left Of The Texture and Quad
		// Back Face
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f(-0.5f, -0.5f, -0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f(-0.5f,  0.5f, -0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y ); glVertex3f( 0.5f,  0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f( 0.5f, -0.5f, -0.5f);	// Bottom Left Of The Texture and Quad
		// Top Face
		glTexCoord2f(upleft.x + sizecell, upleft.y ); glVertex3f(-0.5f,  0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f(-0.5f,  0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y ); glVertex3f( 0.5f,  0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f( 0.5f,  0.5f, -0.5f);	// Top Right Of The Texture and Quad
		// Bottom Face
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f(-0.5f, -0.5f, -0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f( 0.5f, -0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f( 0.5f, -0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f(-0.5f, -0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		// Right face
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f( 0.5f, -0.5f, -0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f( 0.5f,  0.5f, -0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f( 0.5f,  0.5f,  0.5f);	// Top Left Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f( 0.5f, -0.5f,  0.5f);	// Bottom Left Of The Texture and Quad
		// Left Face
		glTexCoord2f(upleft.x , upleft.y ); glVertex3f(-0.5f, -0.5f, -0.5f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y); glVertex3f(-0.5f, -0.5f,  0.5f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(upleft.x + sizecell, upleft.y + sizecell); glVertex3f(-0.5f,  0.5f,  0.5f);	// Top Right Of The Texture and Quad
		glTexCoord2f(upleft.x , upleft.y + sizecell); glVertex3f(-0.5f,  0.5f, -0.5f);	// Top Left Of The Texture and Quad
		glEnd();

		renderer.popState();
		renderer.popMatrix();
	}
	
	// Crosshair (In Ortho View) + pseudo UI
	if(!m_build)
	{
		renderer.pushState();

		renderer.pushMatrix();
		renderer.setMatrixMode(dw::graphics::Renderer::PROJECTION);
		
		renderer.pushMatrix();
		renderer.loadIdentity();

		renderer.setOrtho2D(0, window.getWidth(), 0, window.getHeight(), -1, 1);
		renderer.setMatrixMode(dw::graphics::Renderer::MODELVIEW);						

		renderer.setRenderState(dw::graphics::Renderer::RS_DEPTH_TEST, false);
		renderer.setRenderState(dw::graphics::Renderer::RS_LIGHTING, false);
		renderer.setRenderState(dw::graphics::Renderer::RS_ALPHA_TEST, true);
		renderer.setRenderState(dw::graphics::Renderer::RS_ALPHA_FUNC, dw::graphics::Renderer::GREATER);


		dw::graphics::ITexture* crosshair = dw::graphics::TextureManager::getInstance().loadTexture("crosshair.png");
		renderer.setTexture(crosshair);

		renderer.loadIdentity();
		glTranslatef(window.getWidth()/2.0f,window.getHeight()/2.0f,0.0f);
	
		glBegin(GL_QUADS);									
			glTexCoord2f(0,0); glVertex3f(-32,-32,0.0f);	
			glTexCoord2f(1,0); glVertex3f( 32,-32,0.0f);	
			glTexCoord2f(1,1); glVertex3f( 32, 32,0.0f);	
			glTexCoord2f(0,1); glVertex3f(-32, 32,0.0f);
		glEnd();	


		//UI 
		renderer.loadIdentity();
	
		dw::graphics::ITexture* terrain = dw::graphics::TextureManager::getInstance().getTexture("terrain.png");
		renderer.setTexture(terrain);
	
		if(m_pSpellBar)
		{
			m_pSpellBar->updateGUI();

			renderer.setVertexBuffer(m_pSpellBar->getVertexBuffer());
			renderer.setIndexBuffer(m_pSpellBar->getIndexBuffer());
			renderer.drawIndexedPrimitive(dw::graphics::primitive::triangles, 0, m_pSpellBar->getTrianglesCount() * 3);
		}

		if(m_pInventaire && m_showInventaire)
		{	
			m_pInventaire->updateGUI();

			renderer.setVertexBuffer(m_pInventaire->getVertexBuffer());
			renderer.setIndexBuffer(m_pInventaire->getIndexBuffer());
			renderer.drawIndexedPrimitive(dw::graphics::primitive::triangles, 0, m_pInventaire->getTrianglesCount() * 3);
		}

		renderer.setMatrixMode(dw::graphics::Renderer::PROJECTION);					
		renderer.popMatrix();	
		renderer.setMatrixMode(dw::graphics::Renderer::MODELVIEW);
		renderer.popMatrix();	

		renderer.popState();
	}
	else
	{
		renderer.pushState();

		renderer.pushMatrix();
		renderer.setMatrixMode(dw::graphics::Renderer::PROJECTION);

		renderer.pushMatrix();
		renderer.loadIdentity();

		renderer.setOrtho2D(0, window.getWidth(), 0, window.getHeight(), -1, 1);
		renderer.setMatrixMode(dw::graphics::Renderer::MODELVIEW);						

		renderer.setRenderState(dw::graphics::Renderer::RS_DEPTH_TEST, false);
		renderer.setRenderState(dw::graphics::Renderer::RS_LIGHTING, false);
		renderer.setRenderState(dw::graphics::Renderer::RS_ALPHA_TEST, true);
		renderer.setRenderState(dw::graphics::Renderer::RS_ALPHA_FUNC, dw::graphics::Renderer::GREATER);

		
		dw::graphics::ITexture* crosshair = dw::graphics::TextureManager::getInstance().loadTexture("crosshair.png");
		renderer.setTexture(crosshair);

		renderer.loadIdentity();
		glTranslatef(window.getWidth()/2.0f,window.getHeight()/2.0f,0.0f);

		glBegin(GL_QUADS);									
		glTexCoord2f(0,0); glVertex3f(-32,-32,0.0f);	
		glTexCoord2f(1,0); glVertex3f( 32,-32,0.0f);	
		glTexCoord2f(1,1); glVertex3f( 32, 32,0.0f);	
		glTexCoord2f(0,1); glVertex3f(-32, 32,0.0f);
		glEnd();

		renderer.setRenderState(dw::graphics::Renderer::RS_BLENDING, false);
		
		const WorldGridManager& worldManager = WorldGridManager::getInstance();
		const MaterialLibrary& materialLib = worldManager.getPlanet()->getMaterialLibrary();
		const Material& material = materialLib.getMaterialFromId(m_blockType);

		renderer.loadIdentity();
		glTranslatef(72,72,0);

		dw::maths::Vector2f texcoord = material.getOffsetTop();
		float sizecell = 1/16.0f;

		dw::graphics::ITexture* terrain = dw::graphics::TextureManager::getInstance().getTexture("terrain.png");
		renderer.setTexture(terrain);
		renderer.setRenderState(dw::graphics::Renderer::RS_BLENDING, false);
		glColor4f(1.0f,1.0f,1.0f,1.0f);

		glBegin(GL_QUADS);									
		glTexCoord2f(texcoord[0]		 ,texcoord[1]		  ); glVertex3f(-32,-32,0.0f);	
		glTexCoord2f(texcoord[0]+sizecell,texcoord[1]		  ); glVertex3f( 32,-32,0.0f);	
		glTexCoord2f(texcoord[0]+sizecell,texcoord[1]+sizecell); glVertex3f( 32, 32,0.0f);	
		glTexCoord2f(texcoord[0]		 ,texcoord[1]+sizecell); glVertex3f(-32, 32,0.0f);
		glEnd();	

		renderer.setMatrixMode(dw::graphics::Renderer::PROJECTION);					
		renderer.popMatrix();	
		renderer.setMatrixMode(dw::graphics::Renderer::MODELVIEW);
		renderer.popMatrix();		
		renderer.popState();
	}

	renderer.popMatrix();
	renderer.popState();

}

void FlyBy::term()
{
	InputManager& inputManager(InputManager::getInstance());

	inputManager.unregisterCommand(&m_changeLightingMode);
	inputManager.unregisterCommand(&m_save);


	inputManager.unregisterCommand(&m_camDebug);
	inputManager.unregisterCommand(&m_camFps);


	inputManager.unregisterCommand(&m_leftClick);
	inputManager.unregisterCommand(&m_rightClick);
	

	inputManager.unregisterCommand(&m_chooseBlock);
	
	inputManager.unregisterCommand(&m_spawnEntity);
	inputManager.unregisterCommand(&m_grenadeEntity);

	inputManager.unregisterCommand(&m_buildaction);
	inputManager.unregisterCommand(&m_inventaireaction);
	inputManager.unregisterCommand(&m_dropAll);
}

} // namespace infinity
